﻿using System;
using System.IO;
using UnityEngine;

namespace GatherSystem
{
    public class MiniPerf : IDisposable
    {
        jvalue[] NullParam = new jvalue[0];
        jvalue[] Param1 = new jvalue[1];

        // android.os.Process
        IntPtr m_ProcessClass = IntPtr.Zero;
        IntPtr m_myPidMethod = IntPtr.Zero;
        IntPtr m_myUidMethod = IntPtr.Zero;

        // android.app.ActivityManager
        IntPtr m_ActivityManagerClass = IntPtr.Zero;
        IntPtr m_ActivityManagerObject = IntPtr.Zero;
        IntPtr m_getProcessMemoryInfoMethod = IntPtr.Zero;

        // android.os.Debug$MemoryInfo
        IntPtr m_MemInfoClass = IntPtr.Zero;
        IntPtr m_getTotalPssMethod = IntPtr.Zero;

        // android.os.BatteryManager
        IntPtr m_BatteryManagerClass = IntPtr.Zero;
        IntPtr m_BatteryManagerObject = IntPtr.Zero;
        IntPtr m_getIntPropertyMethod = IntPtr.Zero;

        bool m_isJavaClassOk = false;

        /// <summary>
        /// MiniPerf consists three parts:
        /// GetPss GetVssAndSwap and GetTemperature.  
        /// 
        /// </summary>
        public MiniPerf()
        {
            AndroidJNI.AttachCurrentThread();

            m_ProcessClass = FindClass("android/os/Process");
            if (m_ProcessClass != IntPtr.Zero)
            {
                m_myPidMethod = AndroidJNI.GetStaticMethodID(m_ProcessClass, "myPid", "()I");
                m_myUidMethod = AndroidJNI.GetStaticMethodID(m_ProcessClass, "myUid", "()I");
            }

            m_ActivityManagerClass = FindClass("android/app/ActivityManager");
            if (m_ActivityManagerClass != IntPtr.Zero)
            {
                m_getProcessMemoryInfoMethod = AndroidJNI.GetMethodID(m_ActivityManagerClass, "getProcessMemoryInfo", "([I)[Landroid/os/Debug$MemoryInfo;");
            }

            m_MemInfoClass = FindClass("android/os/Debug$MemoryInfo");
            if (m_MemInfoClass != IntPtr.Zero)
            {
                m_getTotalPssMethod = AndroidJNI.GetMethodID(m_MemInfoClass, "getTotalPss", "()I");
            }

            m_BatteryManagerClass = FindClass("android/os/BatteryManager");
            if (m_BatteryManagerClass != IntPtr.Zero)
            {
                m_getIntPropertyMethod = AndroidJNI.GetMethodID(m_BatteryManagerClass, "getIntProperty", "(I)I");
            }

            // Context context = com.unity3d.player.UnityPlayer.currentActivity;
            using (AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
            {
                using (AndroidJavaObject context = jc.GetStatic<AndroidJavaObject>("currentActivity"))
                {
                    if (context != null)
                    {
                        // ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
                        AndroidJavaObject activityManager = context.Call<AndroidJavaObject>("getSystemService", "activity");
                        m_ActivityManagerObject = activityManager.GetRawObject();
                        if (m_ActivityManagerObject != IntPtr.Zero)
                        {
                            m_ActivityManagerObject = AndroidJNI.NewGlobalRef(m_ActivityManagerObject);
                        }

                        // BatteryManger
                        AndroidJavaObject batteryManager = context.Call<AndroidJavaObject>("getSystemService", "batterymanager");
                        m_BatteryManagerObject = batteryManager.GetRawObject();
                        if (m_BatteryManagerObject != IntPtr.Zero)
                        {
                            m_BatteryManagerObject = AndroidJNI.NewGlobalRef(m_BatteryManagerObject);
                        }
                    }
                }
            }

            m_isJavaClassOk = m_myPidMethod != IntPtr.Zero
                && m_getProcessMemoryInfoMethod != IntPtr.Zero
                && m_getTotalPssMethod != IntPtr.Zero
                && m_ActivityManagerObject != IntPtr.Zero
                && m_BatteryManagerObject != IntPtr.Zero;
        }

        public void init()
        {
            Debug.Log("lyh m_MiniPerfSDK init");
        }

        /// <summary>
        /// Because the program call some values from JAVA, so we have to call Dispose in the end of 
        /// the program to prevent JAVA memory-leak.
        /// </summary>
        public void Dispose()
        {
            if (m_ProcessClass != IntPtr.Zero)
            {
                AndroidJNI.DeleteGlobalRef(m_ProcessClass);
                m_ProcessClass = IntPtr.Zero;
            }
            if (m_ActivityManagerObject != IntPtr.Zero)
            {
                AndroidJNI.DeleteGlobalRef(m_ActivityManagerObject);
                m_ActivityManagerObject = IntPtr.Zero;
            }
            if (m_BatteryManagerObject != IntPtr.Zero)
            {
                AndroidJNI.DeleteGlobalRef(m_BatteryManagerObject);
                m_BatteryManagerObject = IntPtr.Zero;
            }


            // AndroidJNI.DetachCurrentThread();
            AndroidJNI.DetachCurrentThread();
        }

        /// <summary>
        ///     Get Pss from the application.
        /// </summary>
        /// <returns>
        ///     Return total PSS memory usage in kB.
        /// </returns>
        public long GetPss()
        {
            if (m_isJavaClassOk)
            {
                // Debug.MemoryInfo[] memoryInfoArr = am.getProcessMemoryInfo(new int[] { android.os.Process.myPid() });
                int pid = AndroidJNI.CallStaticIntMethod(m_ProcessClass, m_myPidMethod, NullParam);
                IntPtr pidArr = AndroidJNI.ToIntArray(new int[] { pid });
                Param1[0].l = pidArr;
                IntPtr memoryInfoObjectArray = AndroidJNI.CallObjectMethod(m_ActivityManagerObject, m_getProcessMemoryInfoMethod, Param1);
                IntPtr[] memoryInfoArr = AndroidJNI.FromObjectArray(memoryInfoObjectArray);
                if (memoryInfoArr != null && memoryInfoArr.Length > 0)
                {
                    // Debug.MemoryInfo memoryInfo = memoryInfoArr[0];
                    IntPtr memoryInfo = memoryInfoArr[0];
                    // int pss = memoryInfo.getTotalPss();
                    int pss = AndroidJNI.CallIntMethod(memoryInfo, m_getTotalPssMethod, NullParam);
                    Debug.Log("pss: " + pss);
                    return pss;
                }
                //Debug.Log("GetPss() 用到了较多UNITY的方法，多线程无法使用，待改进");
                //return 12;
            }
            return -1;
        }

        /// <summary>
        ///     Get temperature of the device.
        /// </summary>
        /// <returns>
        ///     temperature which represents the device temperature.
        ///     Return the temperature of device in degree Celsius.
        /// </returns>
        public double GetTemperature()
        {
            double temperature = 0;
            int temp = 0;
            TextReader file = null;
            if (File.Exists("/sys/devices/virtual/thermal/thermal_zone0/temp"))
            {
                file = File.OpenText("/sys/devices/virtual/thermal/thermal_zone0/temp");
            }
            else if (File.Exists("/proc/acpi/thermal_zone/THRM/temperature"))
            {
                file = File.OpenText("/proc/acpi/thermal_zone/THRM/temperature");
            }

            Debug.Log("lyh file：" + file);
            if (file != null)
            {
                temp = int.Parse(file.ReadLine());
                Debug.Log("lyh temp：" + temp);
                temperature = Math.Abs((float)temp);
                if (temp >= 100 && temp < 1000)
                {
                    temperature = Math.Round((float)temp / 10);
                }
                else if (temp >= 1000 && temp < 10000)
                {
                    temperature = Math.Round((float)temp / 100);
                }
                else if (temp >= 1000)
                {
                    temperature = Math.Round((float)temp / 1000);
                }
            }
            else
            {
                temperature = 27;
            }
            return temperature;
        }

        /// <summary>
        ///     Get vmSize and vmSwap of virtual memory of the application.
        /// </summary>
        /// <param name="arr">
        ///     vmSize and vmSwap are stored in the arr. 
        ///     arr[0] is vmSize and arr[1] is the vmSwap.
        ///     Get the VmSize and VmSwap in KB.
        /// </param>
        public void GetVssAndSwap(ref int[] arr)
        {
            //  TODO:
            TextReader file = null;
            if (File.Exists("/proc/self/status"))
            {
                file = File.OpenText("/proc/self/status");
            }

            if (file == null)
            {
                return;
            }

            string line;
            int contentPos = 0;
            while ((line = file.ReadLine()) != null)
            {
                if (contentPos == 2)
                {
                    return;
                }

                if (line.IndexOf("VmSize") != -1 || line.IndexOf("VmSwap") != -1)
                {
                    string[] partArr = line.Split(' ');
                    /*Debug.Log("Line:   " + line);*/
                    for (int i = 0; i < partArr.Length; ++i)
                    {
                        // VmSize:  1896748 kB
                        // VmSwap:   115068 kB
                        if (partArr[i].Length != 0 && char.IsDigit(partArr[i][0]))
                        {
                            /*Debug.Log("partArr:" + partArr[i]);*/
                            arr[contentPos] = int.Parse(partArr[i]);
                            contentPos++;
                            break;
                        }
                    }

                }
            }
        }

        /// <summary>
        ///     Get battery current from the device.
        ///     Instantaneous battery current in microamperes, as an integer. 
        ///     Positive values indicate net current entering the battery from a charge source, 
        ///     negative values indicate net current discharging from the battery.
        /// </summary>
        /// <returns>
        ///     Return the current of battery in microamperes.
        /// </returns>
        public int GetCurrent()
        {
            int current = 0;
            jvalue[] param2 = new jvalue[1];
            param2[0].i = 2;
            current = AndroidJNI.CallIntMethod(m_BatteryManagerObject, m_getIntPropertyMethod, param2);
            return current;
        }

        /// <summary>
        ///     Get the class pointer by using class name.
        /// </summary>
        /// <param name="name">
        ///     name represents the type of class which we need.
        /// </param>
        /// <returns>
        ///     classPtr represents the pointer of class which we need.
        /// </returns>
        private IntPtr FindClass(string name)
        {
            IntPtr classPtr = AndroidJNI.FindClass(name);
            if (classPtr != IntPtr.Zero)
                classPtr = AndroidJNI.NewGlobalRef(classPtr);

            return classPtr;
        }
    }

}

